package nbcp.service

import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import org.springframework.web.multipart.MultipartFile
import org.springframework.web.multipart.MultipartHttpServletRequest
import org.springframework.web.multipart.commons.CommonsMultipartResolver
import nbcp.base.comm.*
import nbcp.base.extend.*
import nbcp.base.utils.*
import nbcp.db.IdName
import nbcp.db.mongo.entity.*
import nbcp.db.mongo.*

import nbcp.model.IdUrlMsg
import java.io.File
import java.io.FileInputStream
import java.io.InputStream
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import javax.imageio.ImageIO
import javax.servlet.http.HttpServletRequest

/**
 * 参数传递过程中,都没有 uploadPath 部分.
 */
@Service
open class UploadService {
    companion object {
        private val logger = LoggerFactory.getLogger(this::class.java.declaringClass)
    }

    data class FileNameData(var msg: String = "") {
        var oriName: String = ""
        var extName: String = ""
        var extType: FileExtentionTypeEnum = FileExtentionTypeEnum.Other
        var imgWidth: Int = 0
        var imgHeight: Int = 0

        /*
     * 把文件转移到相应的文件夹下.
     * 1. 第一级目录,按 年-月 归档.
     * 2. 第二级目录,按 企业Id 归档.
     *      2.1.如果是后台, 企业Id = _admin_
     *      2.2.如果是商城用户,企业Id = _shop_
     * 3. 第三级目录,如果是非图片是 extType , 如果是图片是 宽-高
     * 4. 第四级是,如果是图片,是256宽度像素大小的缩略图.
         */
        fun getTargetPaths(): Array<String> {
            var list = mutableListOf<String>()
            list.add(LocalDate.now().format("yyyy-MM"));

            if (this.extType == FileExtentionTypeEnum.Image) {
                list.add("${imgWidth}-${imgHeight}")
            } else {
                list.add(this.extType.toString())
            }

            return list.toTypedArray();
        }

        fun getTargetFileName(): Array<String> {
            return arrayOf(*this.getTargetPaths(), CodeUtil.getCode() + "." + extName);
        }
    }


    @Value("\${shop.uploadServer.path}")
    private var uploadPath = ""

    @Value("\${shop.uploadServer.saveCorp:false}")
    private var saveCorp = false


    fun downloadImage(url: String, corp: IdName, user: IdName, maxWidth: Int = 1200): IdUrlMsg {
        var fileMsg = HttpUtil.getImage(url, uploadPath + File.separator + "_temp_", maxWidth);
        if (fileMsg.msg.HasValue) {
            return IdUrlMsg(msg = fileMsg.msg);
        }


        var vFile = File.separator + "_temp_" + File.separator + fileMsg.name + "." + fileMsg.extName;
        return doUpload(vFile, user, corp.id)
    }


    private fun getFileInfo(file: MultipartFile): FileExtentionInfo {
        var oriFileName = file.originalFilename ?: "";
        if (oriFileName == "blob") {
            oriFileName = file.name;
        }

        oriFileName = oriFileName.Remove("/", "\\", "?", "#", "\"", "'", " ", "%", "&", ":", "@", "<", ">")
        var extInfo = FileExtentionInfo(oriFileName);

        if (oriFileName.length < 4) {
            extInfo.name = "";
        }
        return extInfo;
    }

    fun getFileInfo(request: HttpServletRequest): FileExtentionInfo {
        var oriFileName = request.getHeader("File-Name");
        oriFileName = oriFileName.Remove("/", "\\", "?", "#", "\"", "'", " ", "%", "&", ":", "@", "<", ">")
        var extInfo = FileExtentionInfo(oriFileName);
        if (oriFileName.length < 4) {
            extInfo.name = "";
        }
        return extInfo;
    }

    /**
     * @return 返回相对于 uploadPath 的路径.
     */
    private fun saveTempFile(file: MultipartFile, extName: String): String {
        var ret = ApiResult<String>();

        if (file.size == 0L) {
            throw  Exception("上传的是空文件！");
        }

        var vFile = File.separatorChar + "_temp_" + File.separator + CodeUtil.getCode() + "." + extName;
        var localFile = File(uploadPath + File.separator + vFile)

        if (localFile.parentFile.exists() == false && localFile.parentFile.mkdirs() == false) {
            throw Exception("创建文件夹失败:${localFile.parent}");
        }

        file.transferTo(localFile)
        return vFile;
    }


    /**
     * @return 返回相对于 uploadPath 的路径.
     */
    private fun saveTempFile(file: InputStream, extName: String): String {

        var vFile = File.separatorChar + "_temp_" + File.separator + CodeUtil.getCode() + "." + extName;
        var localFile = File(uploadPath + vFile)

        file.copyTo(localFile.outputStream())
        return vFile
    }


    /**
     * @param vTempFile , 相对于 uploadPath 的相对路径.
     */
    private fun doUpload(vTempFile: String, user: IdName, corpId: String, oriFileName: String = ""): IdUrlMsg {

        var vFile = File(uploadPath + vTempFile);
        if (vFile.exists() == false) {
            return IdUrlMsg("找不到文件:${vFile.FullName}")
        }

        var oriMd5 = getMd5(vFile)
        if (oriMd5.isEmpty()) {
            return IdUrlMsg("计算 Md5 出错:${vFile.FullName}")
        }

        //仅在当前企业下判断,如果重复,则不记录到数据库.
        var md5Annex = mor.system.sysAnnex.query()
                .where { it.checkCode match oriMd5 }
                .toEntity();

        if (md5Annex != null) {
            IdUrlMsg(md5Annex.id, md5Annex.url)
        }

        var extInfo = FileExtentionInfo(vTempFile);

        var fileData = FileNameData();
        fileData.oriName = oriFileName
        fileData.extName = extInfo.extName
        fileData.extType = extInfo.extType


        var annexInfo = SysAnnex();
        annexInfo.ext = extInfo.extName
        annexInfo.size = vFile.length().AsInt()
        annexInfo.checkCode = oriMd5
        annexInfo.createBy = user


        if (extInfo.extType == FileExtentionTypeEnum.Image) {
            val bufferedImage = ImageIO.read(vFile)

            annexInfo.imgWidth = bufferedImage.getWidth();
            annexInfo.imgHeight = bufferedImage.getHeight();

            fileData.imgWidth = annexInfo.imgWidth;
            fileData.imgHeight = annexInfo.imgHeight;
        }

        var targetFileName = File.separator
        if (saveCorp) {
            targetFileName += corpId.AsString("_");
        }
        targetFileName += fileData.getTargetFileName().joinToString(File.separator)

        annexInfo.url = renameFile(vTempFile, targetFileName)

        if (fileData.extType == FileExtentionTypeEnum.Image) {
            ImageUtil.zoomImageScale(uploadPath + annexInfo.url, uploadPath + annexInfo.url + "-var" + File.separator + "256." + fileData.extName, 256, 256)
        }

        annexInfo.url = annexInfo.url.replace("\\", "/");

        if (saveCorp) {
            annexInfo.corpId = corpId
        }

        mor.system.sysAnnex.insert(annexInfo);
        if (mor.affectRowCount == 0) {
            return IdUrlMsg("记录到数据出错")
        }
        return IdUrlMsg(annexInfo.id, annexInfo.url)
    }


    /**
     * 把文件转移到相应的文件夹下.
     * 1. 第一级目录,按 年-月 归档.
     * 2. 第二级目录,按 企业Id 归档.
     *      2.1.如果是后台, 企业Id = admin
     *      2.2.如果是商城用户,企业Id = shop
     * 3. 第三级目录,如果是非图片是 extType , 如果是图片是 宽-高
     * 4. 第四级是,如果是图片,是256宽度像素大小的缩略图.
     */
    private fun renameFile(tempPath: String, targetPath: String): String {
        if (tempPath == targetPath) {
            return targetPath;
        }

        var localFile = File(uploadPath + tempPath);

        if (localFile.exists() == false) {
            throw Exception("找不到保存的文件")
        }
//        var targetFileSects = fileData.getTargetFileName() targetFileSects.joinToString(File.separator)

        var targetFile = File(uploadPath + targetPath)
        if (targetFile.parentFile.exists() == false && targetFile.parentFile.mkdirs() == false) {
            return "创建文件夹失败： ${targetFile.parentFile.FullName}"
        }

        if (localFile.renameTo(targetFile) == false) {
            return "文件重命令名错误: ${localFile.FullName} ,${targetFile.FullName}";
        }


        return targetPath
    }


    private fun getMd5(localFile: File): String {
        var fileStream = FileInputStream(localFile);
        try {
            return Md5Util.getFileMD5(fileStream);
        } finally {
            fileStream.close();
        }
    }

    fun onFileMd5Check(md5: String, user: IdName, corpId: String): IdUrlMsg {
        if (md5.isEmpty()) {
            return IdUrlMsg();
        }

        if (saveCorp) {
            var annex = mor.system.sysAnnex.query()
                    .where { it.checkCode match md5 }
                    .where { it.corpId match corpId }
                    .toEntity()

            if (annex != null) {
                if (File(uploadPath + annex.url).exists()) {
                    return IdUrlMsg(id = annex.id, url = annex.url);
                } else {
                    mor.system.sysAnnex.updateById(annex.id)
                            .set { it.checkCode to "" }
                            .exec();
                }
            }
        }

        var annex = mor.system.sysAnnex.query()
                .where { it.checkCode match md5 }
                .orderBy { it.id.desc }
                .toEntity()

        if (annex == null) {
            return IdUrlMsg();
        }

        if (saveCorp == false) {
            return IdUrlMsg(annex.id, annex.url);
        }

        //copy file & copy record
        var fileData = FileNameData();
        fileData.oriName = annex.name
        fileData.extName = annex.ext
        fileData.extType = FileExtentionInfo(annex.name).extType
        fileData.imgWidth = annex.imgWidth
        fileData.imgHeight = annex.imgHeight


        var targetFileName = fileData.getTargetFileName()
        annex.url = File.separator + targetFileName.joinToString(File.separator)

        var r = File(uploadPath + annex.url).copyTo(File(uploadPath + annex.url))
        if (r.exists() == false) {
            return IdUrlMsg()
        }

        annex.id = "";
        annex.corpId = corpId;
        annex.createAt = LocalDateTime.now()

        mor.system.sysAnnex.insert(annex);
        if (mor.affectRowCount == 0) {
            return IdUrlMsg()
        }

        return IdUrlMsg(annex.id, annex.url);
    }


    fun upload(request_ori: HttpServletRequest, user: IdName, corpId: String, processFile: ((String, FileExtentionTypeEnum) -> Unit)? = null): ListResult<IdUrlMsg> {
        var ret = ListResult<IdUrlMsg>();
        var oriFileExtentionInfo: FileExtentionInfo;

        var multipartResolver = CommonsMultipartResolver(request_ori.getServletContext());

        if (multipartResolver.isMultipart(request_ori)) {

            var multiRequest = multipartResolver.resolveMultipart(request_ori);

            var iter = multiRequest.fileNames
            while (iter.hasNext()) {
                //取得上传文件
                var file = multiRequest.getFile(iter.next());
                if (file == null) continue;

//                //取得当前上传文件的文件名称
//                var myFileName = file.getOriginalFilename();
//                //如果名称不为“”,说明该文件存在，否则说明该文件不存在
//                if (myFileName.HasValue == false) continue;

                oriFileExtentionInfo = getFileInfo(file)
                var vTempFile = saveTempFile(file, oriFileExtentionInfo.extName);
                if (processFile != null) {
                    processFile(uploadPath + vTempFile, oriFileExtentionInfo.extType)
                }
                ret.data.add(doUpload(vTempFile, user, corpId, oriFileExtentionInfo.name + "." + oriFileExtentionInfo.extName));
            }
        } else {
            oriFileExtentionInfo = getFileInfo(request_ori)
            var vTempFile = saveTempFile(request_ori.inputStream, oriFileExtentionInfo.extName)
            if (processFile != null) {
                processFile(uploadPath + vTempFile, oriFileExtentionInfo.extType)
            }

            ret.data.add(doUpload(vTempFile, user, corpId, oriFileExtentionInfo.name + "." + oriFileExtentionInfo.extName));
        }

        return ret;
    }
}